/**
 * Copyright (C) 2011 Andrew C. Love (DNC) <dnc.app.sup@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dnc.cloak.framework.service;

import java.lang.reflect.Proxy;

import org.apache.log4j.Logger;

import com.dnc.cloak.framework.config.ConfigurationConstants;
import com.dnc.cloak.framework.spring.SpringIntegration;
import com.dnc.cloak.framework.util.ClassUtil;
import com.dnc.cloak.service.ConfigurationService;
/**
 * Factory Pattern 
 * Creates services 
 */
public class ServiceFactory {	
	
	private static final String DEFAULT_SERVICE_PACKAGE = "com.dnc.cloak.framework.service";
	private static final Logger logger = Logger.getLogger(ServiceFactory.class.getName());
	private static final ServiceFactory instance = new ServiceFactory();
    		
	private ServiceFactory() {
        super();      

        if(logger.isDebugEnabled()){
			logger.debug("ServiceFactory constructor");
		}    
	}

	public synchronized static ServiceFactory getInstance() {
		return instance;
	}
	
	public Service getService(String serviceName) throws ServiceException{
		return getService(serviceName, DEFAULT_SERVICE_PACKAGE, ServiceType.LOCAL);
	}

    public Service getService(String serviceName, String packageName) throws ServiceException{
        return getService(serviceName, packageName, ServiceType.LOCAL);
    }
    
    @SuppressWarnings("unchecked")
	public <T extends Service> T getService(String serviceName, String packageName, ServiceType serviceType ) throws ServiceException{
    	
		logger.debug(serviceName + ":" + packageName +":" + serviceType);
		
        ConfigurationService configurationService = SpringIntegration.context.getBean("configurationService",ConfigurationService.class);
		
	    String useProxy = configurationService.getProperty(ConfigurationConstants.ROOT_NAMESPACE, "com.dnc.service.createproxy", "true");

        logger.debug("proxy" + ":" + useProxy);

	    if (Boolean.parseBoolean(useProxy)){
	        Service serviceProxy = getProxyService(serviceName, packageName, serviceType);
	        return (T)serviceProxy;
	    }
	    else {
	    	Service localService = null;

    	    switch (serviceType) {
	    		case LOCAL: 
	            	localService = getLocalService(serviceName,packageName);
	            	break;	
	    		case HTTP: 
	    			throw new ServiceException("Service type is currently not supported");
	    		case WEB_SERVICE:
	    			throw new ServiceException("Service type is currently not supported");
	    		case EJB: 
	    			throw new ServiceException("Service type is currently not supported");
	    		default:	 
	    			throw new ServiceException("Service type is currently not supported");
	        }
    	    
    	    return (T)localService;
	    }
	}


    @SuppressWarnings("unchecked")
	private <T extends Service> T getProxyService(String serviceName, String packageName, ServiceType serviceType) throws ServiceException {
        T serviceProxy = null;

	    try {
	        switch (serviceType) {
			case LOCAL: 
				String className = getServiceInterfaceName(serviceName, packageName, serviceType);
				Class<?> serviceInterfaceClass = ClassUtil.forName(className);
	            ServiceLocalInvocationHandler<Service> invocationHandler = new ServiceLocalInvocationHandler<Service>();                
	            Service localService = getLocalService(serviceName,packageName);
	            invocationHandler.setProxiedObject(localService);
				serviceProxy = (T)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Service.class,serviceInterfaceClass }, invocationHandler);
	        	break;	
			case HTTP: 
				throw new ServiceException("Service type is currently not supported");
			case WEB_SERVICE:
				throw new ServiceException("Service type is currently not supported");
			case EJB: 
				throw new ServiceException("Service type is currently not supported");
			default:	 
				throw new ServiceException("Service type is currently not supported");
	        }
	    }
		catch (ClassNotFoundException e) {
			throw new ServiceException(e);
		}
		
		return serviceProxy;
		
    }
	
    private String getServiceInterfaceName(String serviceName, String packageName,ServiceType serviceType){
        String ServiceInterfaceName = "";
        
        ServiceInterfaceName = packageName + "." + "I" + serviceName;
        
        if (ServiceInterfaceName.indexOf("Service") <0 ){
        	ServiceInterfaceName += "Service";
        }

        return ServiceInterfaceName;     
    }

	
	private String getServiceImplName(String serviceName, String packageName,ServiceType serviceType) throws ServiceException {
		String ServicePoolName = "";
		
		switch (serviceType) {
			case LOCAL: 
				ServicePoolName = packageName + ".Local" + serviceName;
		        if (ServicePoolName.indexOf("Service") < 0 ) {
		        	ServicePoolName += "Service";
		        }				
				break;	
			case HTTP: 
				throw new ServiceException("Service type is currently not supported");
			case WEB_SERVICE:
				throw new ServiceException("Service type is currently not supported");
			case EJB: 
				throw new ServiceException("Service type is currently not supported");
			default:	 
				throw new ServiceException("Service type is currently not supported");
		}		
		return ServicePoolName;		
	}
	
	@SuppressWarnings("unchecked")
	private <T extends Service> T getLocalService(String serviceName, String packageName) throws ServiceException{
        T service = null;

        try {
            String className = getServiceImplName(serviceName, packageName, ServiceType.LOCAL);
            service = (T)Class.forName(className).newInstance();
        } 
        catch (ClassNotFoundException e) {
            throw new ServiceException(e);
        }           
        catch (IllegalAccessException e) {
            throw new ServiceException(e);
        }           
        catch (InstantiationException e) {
            throw new ServiceException(e);
        }           
        return service;
    }
    
   
    public void releaseService(String serviceName, Service service ) throws ServiceException{
        releaseService(serviceName, DEFAULT_SERVICE_PACKAGE, service, ServiceType.LOCAL);
    }
    
    public void releaseService(String serviceName, String packageName, Service service) throws ServiceException{
        releaseService(serviceName, packageName, service, ServiceType.LOCAL);
    }
	
	@SuppressWarnings("unchecked")
	public void releaseService(String serviceName, String packageName, Service service, ServiceType serviceType) throws ServiceException{     
        try {
            String className = getServiceImplName(serviceName, packageName, serviceType);
            if (service instanceof Proxy){     
            	ServiceLocalInvocationHandler<Service> invocationHandler = (ServiceLocalInvocationHandler<Service>)Proxy.getInvocationHandler((Proxy)service);
            }
            else {
            }
        } 
        catch (Exception e) {           
            throw new ServiceException(e);
        }       
    }    
 }
